#include "StdAfx.h"

#include <math.h>

#include "SoundBank.h"
#include "Sample.h"
#include "Randomizer.h"

#ifdef _DEBUG
#define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__)
#define new DEBUG_NEW
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#endif

static Randomizer randomizer;

static void SineCb(Sample* sample, unsigned int frequency, unsigned int samplingRate)
{
	double pi2oN = 8.0 * atan(1.0) / ((double)samplingRate/frequency);
	for(unsigned int i=0; i<sample->GetLength(); i++)
	{
		double dval = sin( pi2oN*i );
		sample->GetData()[i] = (short)(dval*SHORT_MAX);
	}
}

static void RandomCb(Sample* sample, unsigned int frequency, unsigned int samplingRate)
{
	for(unsigned int i=0; i<sample->GetLength(); i++)
	{
		sample->GetData()[i] = randomizer.NextShort();
	}
}

static void SawtoothCb(Sample* sample, unsigned int frequency, unsigned int samplingRate)
{
	unsigned int half = sample->GetLength()/2;
	unsigned int i=0;
	for(; i<half; i++)
	{
		double strength = (double)i / (double)half * (double)SHORT_MAX;
		sample->GetData()[i] = (short)(strength - SHORT_MAX);
	}
	for(; i<sample->GetLength(); i++)
	{
		double strength = (double)i / (double)half * (double)SHORT_MAX;
		sample->GetData()[i] = (short)strength;
	}
}

static void SquareCb(Sample* sample, unsigned int frequency, unsigned int samplingRate)
{
	unsigned int i=0;
	for(; i<sample->GetLength()/2; i++)
	{
		sample->GetData()[i] = SHORT_MAX;
	}
	for(; i<sample->GetLength(); i++)
	{
		sample->GetData()[i] = SHORT_MIN;
	}
}


SoundBank::SoundBank(unsigned int samplingRate)
{
	this->samplingRate = samplingRate;
	waveFormNames[Random] = "random";
	waveFormNames[Sawtooth] = "sawtooth";
	waveFormNames[Sine] = "sine";
	waveFormNames[Square] = "square";
	waveFormNames[Triangular] = "triangular";
	waveFormNames[UserFunc1] = "user1";
	waveFormNames[UserFunc2] = "user2";
	waveFormNames[UserFunc3] = "user3";

	userFunc1Cb = RandomCb;
	userFunc2Cb = RandomCb;
	userFunc3Cb = RandomCb;
}

SoundBank::~SoundBank(void)
{
	StringSoundMap::iterator iterator = soundMap.begin();
	while(iterator!=soundMap.end())
	{
		delete iterator->second;
		iterator++;
	}
	soundMap.clear();
}

Sample* SoundBank::GetSample(std::string& name)
{
	Sample* sound = NULL;
	StringSoundMap::iterator iterator = soundMap.find(name);
	if(iterator!=soundMap.end())
	{
		sound = iterator->second;
	}
	return sound;
}

void SoundBank::AddSample(std::string& name, Sample* sound)
{
	soundMap[name] = sound;
}


void SoundBank::SampleName(WaveForm waveForm, unsigned int frequency, double intensity, std::string& destination)
{
	if(intensity<=0 || intensity>1)
	{
		intensity = 0;
	}	
	char buffer[50];
	sprintf_s(buffer, "freq %d int %.1f %s", frequency, intensity, waveFormNames[waveForm].c_str());
	destination = buffer;
}

Sample* SoundBank::GetSample(WaveForm waveForm, unsigned int frequency)
{
	//use standard soundbank for overtones, just multiply frequency
	switch(waveForm)
	{
	case Freq2:
		frequency *= 2;
		waveForm = Sine;
		break;
	case Freq3:
		frequency *= 3;
		waveForm = Sine;
		break;
	case Freq4:
		frequency *= 4;
		waveForm = Sine;
		break;
	case Freq5:
		frequency *= 5;
		waveForm = Sine;
		break;
	case Freq6:
		frequency *= 6;
		waveForm = Sine;
		break;
	case Freq7:
		frequency *= 7;
		waveForm = Sine;
		break;
	case Freq8:
		frequency *= 8;
		waveForm = Sine;
		break;
	case Freq9:
		frequency *= 9;
		waveForm = Sine;
		break;
	default:
		break;//nothing otherwise
	}

	std::string sampleName;
	SampleName(waveForm, frequency, 1, sampleName);
	Sample* sample = GetSample(sampleName);
	if(sample==NULL)
	{		
		switch(waveForm)
		{
		case Random:
			sample = RandomSample(sampleName, frequency);
			break;
		case Sawtooth:
			sample = SawtoothSample(sampleName, frequency);
			break;
		case Sine:
			sample = SineSample(sampleName, frequency);
			break;
		case Square:
			sample = SquareSample(sampleName, frequency);
			break;
		case Triangular:
			sample = TriangularSample(sampleName, frequency);
			break;
		case UserFunc1:
			sample = UserFunc1Sample(sampleName, frequency);
			break;
		case UserFunc2:
			sample = UserFunc2Sample(sampleName, frequency);
			break;
		case UserFunc3:
			sample = UserFunc3Sample(sampleName, frequency);
			break;
		}
	}
	return sample;
}

Sample* SoundBank::GetSample(WaveForm waveForm, unsigned int frequency, void(*SampleCb)(Sample*,unsigned int,unsigned int))
{
	std::string sampleName;
	SampleName(waveForm, frequency, -1, sampleName);
	Sample* sample = GetSample(sampleName);
	if(sample==NULL)
	{		
		sample = CreateAndAddSample(sampleName, frequency, SampleCb);
	}
	return sample;
}

Sample* SoundBank::GetSample(WaveForm waveForm, unsigned int frequency, double intensity)
{
	std::string sampleName;
	SampleName(waveForm, frequency, intensity, sampleName);
	Sample* sample = GetSample(sampleName);
	if(sample==NULL)
	{
		Sample* baseSample = GetSample(waveForm, frequency);
		sample = new Sample(baseSample->GetLength());
		baseSample->CopyTo(*sample, intensity);
		AddSample(sampleName, sample);
	}
	return sample;
}

Sample* SoundBank::GetSample(WaveForm waveForm, unsigned int frequency, double intensity, 
							 void(*SampleCb)(Sample*,unsigned int,unsigned int))
{
	std::string sampleName;
	SampleName(waveForm, frequency, intensity, sampleName);
	Sample* sample = GetSample(sampleName);
	if(sample==NULL)
	{
		Sample* baseSample = GetSample(waveForm, frequency, SampleCb);
		sample = new Sample(baseSample->GetLength());
		baseSample->CopyTo(*sample, intensity);
		AddSample(sampleName, sample);
	}
	return sample;
}

void SoundBank::SampleLoop(Sample* sample, unsigned int phaseCount, void(*SampleLoopCb)(Sample*,unsigned int,unsigned int,unsigned int))
{
	unsigned int phaseLength = sample->GetLength()/phaseCount;
	unsigned int phasePos = 0;
	unsigned int phase = 0;
	for(unsigned int i=0; i<sample->GetLength(); i++, phasePos++)
	{
		if(phasePos>=phaseLength)
		{
			phase++;
			phasePos = 0;
		}
		SampleLoopCb(sample, phaseLength, phase, phasePos);
	}
}

Sample* SoundBank::CreateAndAddSample(std::string& name, unsigned int frequency, void(*SampleCb)(Sample*,unsigned int,unsigned int))
{
	unsigned int frames = (unsigned int)((double)samplingRate)/frequency;
	Sample* sample = new Sample(frames);
	if(SampleCb) SampleCb(sample, frequency, samplingRate);
	AddSample(name, sample);
	return sample;
}



static void TriangularCb(Sample* sample, unsigned int phaseLength, unsigned int phase, unsigned int phasePos)
{
	double strength = ((double)phasePos/(double)phaseLength)*SHORT_MAX;
	int roundedStrength = (int)strength;
	int add = 0;
	switch(phase) {
		case 0:
			add = roundedStrength;
			break;
		case 1:
			add = SHORT_MAX-roundedStrength;
			break;
		case 2:
			add -= roundedStrength;
			break;
		case 3:
			add = roundedStrength-SHORT_MAX;
			break;
	}
	sample->GetData()[(phase*phaseLength)+phasePos] = (short)add;
}


Sample* SoundBank::SineSample(std::string& name, unsigned int frequency)
{
	return CreateAndAddSample(name, frequency, SineCb);
}

Sample* SoundBank::TriangularSample(std::string& name, unsigned int frequency)
{
	Sample* sample = CreateAndAddSample(name, frequency, NULL);
	SampleLoop(sample, 4, TriangularCb);
	return sample;
}

Sample* SoundBank::RandomSample(std::string& name, unsigned int frequency)
{
	return CreateAndAddSample(name, frequency, RandomCb);
}

Sample* SoundBank::SquareSample(std::string& name, unsigned int frequency)
{
	return CreateAndAddSample(name, frequency, SquareCb);
}

Sample* SoundBank::SawtoothSample(std::string& name, unsigned int frequency)
{
	return CreateAndAddSample(name, frequency, SawtoothCb);
}



Sample* SoundBank::UserFunc1Sample(std::string& name, unsigned int frequency)
{
	return CreateAndAddSample(name, frequency, userFunc1Cb);
}

Sample* SoundBank::UserFunc2Sample(std::string& name, unsigned int frequency)
{
	return CreateAndAddSample(name, frequency, userFunc2Cb);
}

Sample* SoundBank::UserFunc3Sample(std::string& name, unsigned int frequency)
{
	return CreateAndAddSample(name, frequency, userFunc3Cb);
}
